﻿#ifndef ___PCLIB_S_HTTP___
#define ___PCLIB_S_HTTP___


#include "../m_core/m_core.hpp"




/**
*@brief    URL类，管理url资源和解析
*
*/
class CPCHttpUrl
{
public:
    explicit CPCHttpUrl(){}
    ~CPCHttpUrl(){}

    /**
    *@brief     判断另外一个url是否和这个url指向同样的地址
    *@return    是否相同
    */
    bool SameAddr(const CPCHttpUrl& another)
    {
        return (m_Schema == another.m_Schema   && m_Host == another.m_Host  && m_Port == another.m_Port);
    }

    /**
    *@brief     是否使用了默认端口：http 80，https 443
    *@return    是否使用了默认端口
    */
    bool IsDefaultPort() const
    {
        return ((m_Schema == "http" && m_Port == 80) || (m_Schema == "https" && m_Port == 443));
    }

    /**
    *@brief     URL解析
    *@return    是否成功，成功则数据保存在成员变量
    */
    CPCResult<> Parse(const char * url)
    {
        //参数校验
        CPCResult<> result;
        if (NULL == url || url[0] == 0)
        {
            return result.SetFail("Parse url fail！  url is empty.");
        }
        size_t url_len = strlen(url);
        if (url_len > PC_MAXURL)
        {
            return result.SetFail("url_len(%lu) > PC_MAXURL(%d)！  url too long.", url_len, PC_MAXURL);
        }

        //解析URL
        m_Schema = "";
        m_Host = "";
        m_Port = 0;
        m_Path = "";
        m_Query = "";
        m_szFragment = "";
        m_szUserInfo = "";
        pchttpparser::http_parser_url up;
        pchttpparser::http_parser_url_init(&up);
        int ret = pchttpparser::http_parser_parse_url(url, url_len, 0, &up);
        if (ret)
        {
            return result.SetFail("http_parser_parse_url(%s) fail！  ", url);
        }
        //HTTP&HTTPS
        if (up.field_set & (1 << pchttpparser::UF_SCHEMA))
        {
            m_Schema.assign(url, up.field_data[pchttpparser::UF_SCHEMA].off, up.field_data[pchttpparser::UF_SCHEMA].len);
        }
        else
        {
            return result.SetFail("http_parser_parse_url(%s) fail, NOT FIND SUPPORTED SCHEMA！  ", url);
        }
        //UF_HOST
        if (up.field_set & (1 << pchttpparser::UF_HOST))
        {
            m_Host.assign(url, up.field_data[pchttpparser::UF_HOST].off, up.field_data[pchttpparser::UF_HOST].len);
        }
        else
        {
            return result.SetFail("http_parser_parse_url(%s) fail ！ host NOT FIND. ", url);
        }
        //UF_PORT
        if (up.field_set & (1 << pchttpparser::UF_PORT))
        {
            m_Port = up.port;
        }
        else
        {
            //没有解析出端口，这个时候要根据UF_SCHEMA进行赋值为默认端口
            if (strcasecmp(m_Schema.c_str(), "http") == 0)
            {
                m_Port = 80;
            }
            else if (strcasecmp(m_Schema.c_str(), "https") == 0)
            {
                m_Port = 443;
            }
            else
            {
                //其他用户自己处理。
                m_Port = 0;
            }
        }
        //UF_PATH
        if (up.field_set & (1 << pchttpparser::UF_PATH))
        {
            m_Path.assign(url, up.field_data[pchttpparser::UF_PATH].off, up.field_data[pchttpparser::UF_PATH].len);
        }
        else
        {
            m_Path = "/";
        }
        //UF_QUERY
        if (up.field_set & (1 << pchttpparser::UF_QUERY))
        {
            m_Query.assign(url, up.field_data[pchttpparser::UF_QUERY].off, up.field_data[pchttpparser::UF_QUERY].len);
        }
        //UF_FRAGMENT
        if (up.field_set & (1 << pchttpparser::UF_FRAGMENT))
        {
            m_szFragment.assign(url, up.field_data[pchttpparser::UF_FRAGMENT].off, up.field_data[pchttpparser::UF_FRAGMENT].len);
        }
        //UF_USERINFO
        if (up.field_set & (1 << pchttpparser::UF_USERINFO))
        {
            m_szUserInfo.assign(url, up.field_data[pchttpparser::UF_USERINFO].off, up.field_data[pchttpparser::UF_USERINFO].len);
        }
        return result;
    }

public:
    std::string m_Schema;
    std::string m_Host;
    uint16_t    m_Port;
    std::string m_Path;
    std::string m_Query;
    std::string m_szFragment;
    std::string m_szUserInfo;
};

/**
*@brief        HTTP头
*/
class CPCHttpHeader
{
public:
    explicit CPCHttpHeader()
    {
        Init();
    }
    ~CPCHttpHeader()
    {
    }

    /**
    *@brief        初始化
    */
    void Init()
    {
        m_req_method = "GET";
        m_req_url = "/";
        m_http_major = 1;
        m_http_minor = 1;
        m_resp_httpcode = 0;
        m_resp_codedesc = "";
        m_header.clear();
    }

    /**
    *@brief        转化为字符串
    *@param        isReq    [in]    true为请求；false为响应
    *@param        isEnd    [in]    结束请求头
    */
    std::string ToString(bool isReq, bool isEnd)  const
    {
        //请求/回应行
        std::string strFixedHeader;
        if (isReq)
        {
            char szLine[PC_MAXURL] = { 0 };
            snprintf(szLine, sizeof(szLine) - 1, "%s %s HTTP/%u.%u\r\n", m_req_method.c_str(), m_req_url.c_str(), m_http_major, m_http_minor);
            strFixedHeader = szLine;
        }
        else
        {
            char szLine[MAX_PATH] = { 0 };
            snprintf(szLine, sizeof(szLine) - 1, "HTTP/%u.%u %u %s\r\n", m_http_major, m_http_minor, m_resp_httpcode, m_resp_codedesc.c_str());
            strFixedHeader = szLine;
        }

        //header键值对
        for (auto it = m_header.begin(); it != m_header.end(); ++it)
        {
            strFixedHeader += (*it).first;
            if (!((*it).second.empty()))
            {
                strFixedHeader += ": ";
                strFixedHeader += (*it).second;
            }
            strFixedHeader += "\r\n";
        }

        //结束
        if (isEnd)
        {
            strFixedHeader += "\r\n";
        }
        return strFixedHeader;
    }

    /**
    *@brief        设置请求行
    *@param        method    [in]    http请求方法
    *@param        httpurl   [in]    CPCHttpUrl对象
    */
    void SetRequestLine(const std::string& method, const CPCHttpUrl& httpurl)
    {
        m_req_method = method.empty() ? "GET" : method;
        m_req_url = httpurl.m_Path;
        if (!httpurl.m_Query.empty())
        {
            m_req_url += "?";
            m_req_url += httpurl.m_Query;
        }
    }

    /**
    *@brief        设置请求行
    *@param        method    [in]    http请求方法
    *@param        url       [in]    url字符串
    */
    void SetRequestLine(const std::string& method, const std::string& url)
    {
        m_req_method = method.empty() ? "GET" : method;
        m_req_url = url;
    }

    /**
    *@brief        设置主机
    *@param        httpurl    [in]    CPCHttpUrl对象
    */
    void SetHost(const CPCHttpUrl& httpurl)
    {
        char szHostValue[64] = { 0 };
        if (httpurl.IsDefaultPort())
            snprintf(szHostValue, sizeof(szHostValue) - 1, "%s", httpurl.m_Host.c_str());
        else
            snprintf(szHostValue, sizeof(szHostValue) - 1, "%s:%u", httpurl.m_Host.c_str(), httpurl.m_Port);
        m_header["Host"] = szHostValue;
    }

    /**
    *@brief        设置主机
    *@param        url        [in]    url字符串
    */
    CPCResult<> SetHost(const std::string& url)
    {
        CPCHttpUrl httpurl;
        auto result = httpurl.Parse(url.c_str());
        if (result)
        {
            SetHost(httpurl);
        }
        return result;
    }

    /**
    *@brief        设置body长度
    *@param        length    [in]    body长度
    */
    void SetContentLength(size_t length)
    {
        char szContentLengthValue[16] = { 0 };
        snprintf(szContentLengthValue, sizeof(szContentLengthValue) - 1, "%u", (unsigned int)length);
        m_header["Content-Length"] = szContentLengthValue;
    }

    /**
    *@brief        设置Connection为keep-alive
    *@param        bkeepalive    [in]    是否为keep-alive
    */
    void SetKeepAlive(bool bkeepalive)
    {
        m_header["Connection"] = (bkeepalive ? "Keep-Alive" : "Close");
    }

    /**
    *@brief        设置UserAgent
    *@param        ua    [in]    用户agent
    */
    void SetUserAgent(const char* ua = "Mozilla/4.0")
    {
        if (ua != NULL && ua[0] != 0)
        {
            m_header["User-Agent"] = ua;
        }
    }

    /**
    *@brief        设置请求Range按字节分段下载
    *@param        start        [in]    起始字节【包括】
    *@param        end          [in]    结束字节【包括】
    */
    void SetRangeBytes(unsigned int start, unsigned int end)
    {
        if (start > end)
            return;
        char szRangeValue[64] = { 0 };
        snprintf(szRangeValue, sizeof(szRangeValue) - 1, "bytes=%u-%u", start, end);
        m_header["Range"] = szRangeValue;
    }

    /**
    *@brief     设置ContentType为form-data;boundary用于文件上传
    *@return    生成的boundary
    */
    std::string SetContentTypeBoundary()
    {
        std::string boundary = CPCRandom::PCGenString(50);
        m_header["Content-Type"] = "multipart/form-data; boundary=" + boundary;
        return boundary;
    }

    /**
    *@brief        添加或替换已有的http头键值对
    *@param        key      [in]    键
    *@param        value    [in]    值
    */
    void SetHeader(const char* key, const char* value)
    {
        if (key != NULL && value != NULL && key[0] != 0 && value[0] != 0)
        {
            m_header[key] = value;
        }
    }

    /**
    *@brief     获取头里面的指定值，如果有重复只获取其中一个
    *@param     key     [in]    键
    *@param     icase   [in]    是否忽略键的大小写
    *@return    值，获取失败为空
    */
    std::string GetHeaderValue(const char* key, bool icase = true)
    {
        if (key == NULL || key[0] == 0)
            return "";
        for (auto it = m_header.begin(); it != m_header.end(); ++it)
        {
            const char* tmpkey = it->first.c_str();
            if (icase)
            {
                if (strcasecmp(tmpkey, key) == 0)
                {
                    return it->second;
                }
            }
            else
            {
                if (strcmp(tmpkey, key) == 0)
                {
                    return it->second;
                }
            }
        }
        return "";
    }

    /**
    *@brief     是否为keep-alive，Connection为空/Close认为否，其他都认为是
    *@return    是否为keep-alive
    */
    bool IsKeepAlive()
    {
        //没有指定Connection，认为不是keep-alive
        if (m_header.find("Connection") == m_header.end())
            return false;
        std::string value = m_header["Connection"];
        if (value.empty())
            return false;
        return (strcasecmp(value.c_str(), "Close") != 0);
    }

    /**
    *@brief     获取响应码
    *@return    响应码
    */
    unsigned int GetRetCode()
    {
        return m_resp_httpcode;
    }

    /**
    *@brief     获取Content-Length
    *@return    Content-Length长度
    */
    unsigned int GetContentLength()
    {
        auto iter = m_header.find("Content-Length");
        if (iter == m_header.end())
            return 0;
        int length = atoi((*iter).second.c_str());
        if (length < 0)
            return 0;
        return (unsigned int)length;
    }

    /**
    *@brief     获取Content-disposition中服务器返回给客户端的附件文件名
    *@return    文件名。如果服务器没有返回文件名，返回空。
    */
    std::string GetContentDisposition()
    {
        auto iter = m_header.find("Content-disposition");
        if (iter == m_header.end())
        {
            iter = m_header.find("Content-Disposition");
            if (iter == m_header.end())
            {
                return "";
            }
        }
        const char* value = (*iter).second.c_str();
        const char* pfilename = strstr(value, "filename=");
        if (pfilename == NULL)
        {
            return "";
        }
        return CPCStrUtil::PCStrTrim(pfilename + 9, "\"");
    }

    /**
    *@brief     根据服务器返回的AcceptRange数据进行解析如Content-Range: bytes 0-100/64989
    *@param     start        [out]    输出起始字节【包括】
    *@param     end          [out]    输出结束字节【包括】
    *@param     all          [out]    输出整个文件长度
    *@param     iscomplete   [out]    输出是否接收完成了所有数据
    *@return    是否出现了异常
    */
    CPCResult<> GetAcceptRangeBytesInfo(unsigned int& start, unsigned int& end, unsigned int& all, bool& iscomplete)
    {
        CPCResult<> result;
        if (m_resp_httpcode == 200)
        {
            //可能是服务器不支持AcceptRange，把数据全部返回来了
            all = GetContentLength();
            start = 0;
            end = (all > 0) ? (all - 1) : 0;
            iscomplete = true;
            return result;
        }
        else if (m_resp_httpcode == 206)
        {
            auto iter = m_header.find("Content-Range");
            if (iter == m_header.end())
            {
                //服务器返回的和协议不符合，206应该附带Content-Range
                return result.SetFail("server return 206 but NOT Content-Range");
            }
            if ((*iter).second.length() < 11 || (*iter).second.length() >= 64)
            {
                return result.SetFail("server return 206 but Content-Range length error: %s", (*iter).second.c_str());
            }
            char szRangeInfo[64] = { 0 };
            strcpy(szRangeInfo, (*iter).second.c_str());
            if (strncasecmp(szRangeInfo, "bytes ", 6) != 0)
            {
                return result.SetFail("server return 206 but Content-Range first 6 bytes format error: %s", (*iter).second.c_str());
            }
            int rod = -1;    //'-'位置
            int slash = -1;    //'/'位置
            for (size_t i = 6 + 1; i < (*iter).second.length() - 1; ++i)
            {
                if (szRangeInfo[i] == '-')        { rod = (int)i;    szRangeInfo[i] = 0; }
                else if (szRangeInfo[i] == '/')    { slash = (int)i;    szRangeInfo[i] = 0; }
            }
            if (rod == -1 || slash == -1 || slash - rod < 2)
            {
                return result.SetFail("server return 206 but Content-Range format error: %s", (*iter).second.c_str());
            }
            start = atoi(szRangeInfo + 6);
            end = atoi(szRangeInfo + rod + 1);
            all = atoi(szRangeInfo + slash + 1);
            if (start > end || start >= all || end >= all)
            {
                return result.SetFail("server return 206 but Content-Range number(start:%u,end:%u,all:%u) error: %s", start, end, all, (*iter).second.c_str());
            }
            iscomplete = (end == all - 1);
            return result;
        }
        else
        {
            return result.SetFail("m_resp_httpcode != 200 && m_resp_httpcode != 206, is : %u ", m_resp_httpcode);
        }
    }

public:
    std::string  m_req_method;      //请求才有    GET, POST ..
    std::string  m_req_url;         //请求才有    /index.html , etc
    unsigned short m_http_major;    //主版本号    1    (HTTP/1.1)
    unsigned short m_http_minor;    //次版本号    1    (HTTP/1.1)
    unsigned int m_resp_httpcode;   //回应才有    200 404 ..
    std::string  m_resp_codedesc;   //回应才有    OK ..
    mutable std::unordered_map<std::string, std::string> m_header;
};

/**
*@brief  RESTFUL HTTP协议解析类，负责http解包
*        解包功能基于note.js 的 http_parser模块
*/
class CPCRestHttpUnPack : CPCNoCopyable
{
public:
    /**
    *@brief        解包请求init
    *@param        req [in]    true解析请求；false解析回应
    */
    void UnPackInit(bool req)
    {
        //数据    
        m_header.Init();
        m_body.clear();
        m_LastResult.SetSucc(0);
        m_cachedkey.clear();
        m_cachedvalue.clear();

        //设置
        m_parser_settings.on_message_begin = CPCRestHttpUnPack::OnMessageBegin;
        m_parser_settings.on_url = CPCRestHttpUnPack::OnReqUrl;
        m_parser_settings.on_status = NULL;
        m_parser_settings.on_header_field = CPCRestHttpUnPack::OnField;
        m_parser_settings.on_header_value = CPCRestHttpUnPack::OnValue;
        m_parser_settings.on_headers_complete = NULL;
        m_parser_settings.on_body = CPCRestHttpUnPack::OnBody;
        m_parser_settings.on_message_complete = CPCRestHttpUnPack::OnBodyCompelete;
        m_parser_settings.on_chunk_header = NULL;
        m_parser_settings.on_chunk_complete = NULL;
        http_parser_init(&m_parser, req ? pchttpparser::HTTP_REQUEST : pchttpparser::HTTP_RESPONSE);
        m_parser.data = this;
    }

    /**
    *@brief     解包请求update
    *@param     data    [in]    数据
    *@param     len     [in]    长度
    *@return    无，调用后从m_LastResult拿结果
    */
    void UnPackUpdate(const char* data, size_t len)
    {
        size_t nparsed = http_parser_execute(&m_parser, &m_parser_settings, data, len);

        if (0 != m_parser.http_errno)
        {
            m_LastResult.SetFail(m_parser.http_errno,"http_parser_execute err(err:%s)！ len=(%u) ", pchttpparser::http_errno_description((pchttpparser::http_errno)m_parser.http_errno), len);
            return;
        }
        if (nparsed > len)
        {
            m_LastResult.SetFail(-1,"http_parser_execute err！ nparsed(%u) > len(%u) ", (unsigned int)nparsed, (unsigned int)len);
            return;
        }
        if (nparsed < len)
        {
            //没有解析这么多，收到的数据有多余数据
            printf("http_parser_execute warning！ nparsed(%u) < len(%u) ", (unsigned int)nparsed, (unsigned int)len);
            m_LastResult.SetSucc(1);
            return;
        }
    }

    /**
    *@brief     取http头的字符串
    *@return    http头
    */
    std::string GetHeaderString() const
    {
        if (m_parser.type == pchttpparser::HTTP_REQUEST)
        {
            return m_header.ToString(true, true);
        }
        else
        {
            return m_header.ToString(false, true);
        }
    }

    /**
    *@brief     取http头的字符串
    *@return    http头
    */
    const char* GetBodyStr() const
    {
        return m_body.c_str();
    }

    /**
    *@brief     取URL的字符串
    *@return    http头
    */
    const char* GetURLStr() const
    {
        return m_header.m_req_url.c_str();
    }

    /**
    *@brief     取url的参数列表
    *@return    请求参数列表
    */
    std::unordered_map<std::string, std::string> GetURLParameter() const
    {
        std::unordered_map<std::string, std::string> ret;
        const char* url = m_header.m_req_url.c_str();
        const char* pQue = strstr(url, "?");
        if (pQue != NULL && pQue[1] != 0)
        {
            auto kvs = CPCStrUtil::PCStrSplit(pQue + 1, "&");
            for (auto it = kvs.begin(); it != kvs.end(); ++it)
            {
                //实际数据
                std::string strLene = (*it);
                size_t nEqualPos = 0;
                for (size_t i = 0; i < strLene.length(); i++)
                {
                    if (strLene[i] == '=')
                    {
                        nEqualPos = i;
                        break;
                    }
                }
                if (nEqualPos == 0)
                {
                    //没找到等号或等号在第一个字符，忽略
                    continue;
                }
                std::string first = std::string(strLene.c_str(), (int)nEqualPos);
                std::string second = std::string(strLene.c_str() + nEqualPos + 1);
                ret.insert(std::make_pair(first, second));
            }
        }

        return ret;
    }

    /**
    *@brief     接收到的数据为空
    *@return    是否为空
    */
    bool Empty() const
    {
        return m_header.m_header.empty() && m_header.m_resp_httpcode == 0;
    }

public:
    /**
    *@brief      解析完成后，在这里取解析结果
    *            最后的解析结果是否出错。 失败：出错了；成功：int[0处理中 1处理完成]
    */
    CPCResult<int>    m_LastResult;

    //结果
    CPCHttpHeader m_header;
    std::string   m_body;

protected:
    static int OnMessageBegin(pchttpparser::http_parser* parser)
    {
        CPCRestHttpUnPack * pThis = (CPCRestHttpUnPack*)parser->data;
        //pThis->UnPackInit();
        return 0;
    }
    static int OnReqUrl(pchttpparser::http_parser* parser, const char *at, size_t length)
    {
        CPCRestHttpUnPack * pThis = (CPCRestHttpUnPack*)parser->data;
        pThis->m_header.m_req_url.assign(at, length);
        return 0;
    }
    static int OnField(pchttpparser::http_parser* parser, const char *at, size_t length)
    {
        CPCRestHttpUnPack * pThis = (CPCRestHttpUnPack*)parser->data;
        if (!pThis->m_cachedkey.empty() && !pThis->m_cachedvalue.empty())
        {
            pThis->m_header.SetHeader(pThis->m_cachedkey.c_str(), pThis->m_cachedvalue.c_str());
            pThis->m_cachedkey.clear();
            pThis->m_cachedvalue.clear();
        }
        pThis->m_cachedkey.append(at, length);
        return 0;
    }
    static int OnValue(pchttpparser::http_parser* parser, const char *at, size_t length)
    {
        CPCRestHttpUnPack * pThis = (CPCRestHttpUnPack*)parser->data;
        pThis->m_cachedvalue.append(at, length);
        return 0;
    }
    static int OnBody(pchttpparser::http_parser* parser, const char *at, size_t length)
    {
        CPCRestHttpUnPack * pThis = (CPCRestHttpUnPack*)parser->data;
        pThis->m_body.append(at, length);
        return 0;
    }
    static int OnBodyCompelete(pchttpparser::http_parser* parser)
    {
        CPCRestHttpUnPack * pThis = (CPCRestHttpUnPack*)parser->data;
        if (!pThis->m_cachedkey.empty() && !pThis->m_cachedvalue.empty())
        {
            pThis->m_header.SetHeader(pThis->m_cachedkey.c_str(), pThis->m_cachedvalue.c_str());
        }
        pThis->m_LastResult.SetSucc(1);
        if (pThis->m_parser.type == pchttpparser::HTTP_REQUEST)
        {
            pThis->m_header.m_req_method = CPCStaticDeclare::obj().GetHttpMethodStr(pThis->m_parser.method);
        }
        else
        {
            pThis->m_header.m_resp_httpcode = pThis->m_parser.status_code;
            pThis->m_header.m_resp_codedesc = CPCStaticDeclare::obj().GetHttpStatusCodeStr(pThis->m_parser.status_code);
        }
        pThis->m_header.m_http_major = pThis->m_parser.http_major;
        pThis->m_header.m_http_minor = pThis->m_parser.http_minor;
        return 0;
    }
    pchttpparser::http_parser             m_parser;
    pchttpparser::http_parser_settings   m_parser_settings;
    std::string             m_cachedkey;
    std::string             m_cachedvalue;
};


/**
*@brief      HTTP包体。form-data模式
*            它的http包头为Content-Type: multipart/form-data; boundary=[N个字节]
*            它的http包体为多个boundary分割的模式，每个分割部分既可以添加键值对，又可以添加文件
*            文件的Content-Type参考 http://tool.oschina.net/commons/
*/
class CPCFormDataBody : CPCNoCopyable
{
public:
    /**
    *@brief       【必须调用】通过用户生成的boundary来初始化
    *@param        boundary    [in]    生成的原始boundary
    */
    void Init(std::string boundary)
    {
        m_boundary = boundary;
        m_boundary.insert(0, "--");
        m_boundary += "\r\n";
        m_parts.clear();
    }

    /**
    *@brief        向body中添加键值对
    *@param        key      [in]    键
    *@param        value    [in]    值
    */
    void AppendKeyValue(const std::string& key, const std::string& value)
    {
        std::string info("Content-Disposition: form-data; name=\"");
        info += key;
        info += "\"\r\n\r\n";
        info += value;
        m_parts.push_back(std::make_pair(std::move(info), ""));
    }

    /**
    *@brief     向body中添加文件，此时并不实际读文件
    *@param     name        [in]    为Content-Disposition指定name，这个是键值对的键名，不是文件名
    *@param     filepath    [in]    文件全路径
    *@return    是否成功
    */
    CPCResult<> AppendFile(const std::string& name, const std::string& filepath)
    {
        CPCResult<> result;
        auto result2 = CPCFileUtil::PCSeperatePath(filepath.c_str());
        if (!result2)
        {
            return result.SetFail("PCSeperatePath fail:%s",result2.ErrDesc());
        }
        if (0 != ::access(filepath.c_str(), F_OK))
        {
            return result.SetFail("file(%s) can Not Accessable.", filepath.c_str());
        }

        std::string info("Content-Disposition: form-data; name=\"");
        info += name;
        info += "\"; filename=\"";
        info += result2.Get().second;
        info += "\"\r\nContent-Type: ";
        info += CPCStaticDeclare::obj().GetHttpMimeType(CPCFileUtil::PCGetFileExt(filepath.c_str()));
        info += "\r\n\r\n";
        m_parts.push_back(std::make_pair(std::move(info), filepath));
        return result;
    }

    /**
    *@brief     计算要发送的报文总长度
    *@return    长度
    */
    size_t GetBodyContentLength()
    {
        size_t length = 0;
        for (auto it = m_parts.begin(); it != m_parts.end(); ++it)
        {
            //boundary和info
            length += m_boundary.length();
            length += (*it).first.length();
            if (!(*it).second.empty())
            {
                //文件长度
                auto result2 = CPCFileUtil::PCGetFileSize((*it).second.c_str());
                if(!result2)
                {
                    return 0;
                }
                //TODO:大文件不支持
                length += (size_t)result2.Get();
            }
            //回车
            length += 2;
        }
        //最后再添加 "--原始boundary--\r\n"长度
        return (length + m_boundary.length() + 2);
    }

    /**
    *@brief     从这个对象读取body
    *@param     callback    [in]    每次读取到body内容时的回调函数。回调函数返回失败时将会退出
    *@param     memsize     [in]    分多次读取文件时每次分配的内存空间大小
    *@return    是否成功
    */
    CPCResult<> ScanBodyContent(const std::function<CPCResult<>(const char* buf, size_t len)> &callback, size_t memsize = PC_MAXHEAP)
    {
        CPCResult<> result;
        for (auto it = m_parts.begin(); it != m_parts.end(); ++it)
        {
            //添加boundary和info
            std::string head = m_boundary + (*it).first;
            result = callback(head.data(), head.length());
            if (!result)
            {
                return result;
            }
            //文件，还需要加上文件内容
            if (!(*it).second.empty())
            {
                result = CPCFileUtil::PCScanFile((*it).second.c_str(), callback, memsize);
                if (!result)
                {
                    return result;
                }
            }
            //最后每个part要添加回车
            result = callback("\r\n", 2);
            if (!result)
            {
                return result;
            }
        }
        //最后再添加 "--原始boundary--\r\n"
        m_boundary.insert(m_boundary.length() - 2, "--");
        return callback(m_boundary.data(), m_boundary.length());
    }

protected:
    std::string m_boundary;    //body里面的m_boundary，需要在原始m_boundary前面加--，后面加\r\n
    std::vector<std::pair<std::string, std::string> > m_parts;    //多个part的列表，pair格式：【part信息，文件名(如果是文件，否则为空)】
};






/**
*@brief     RESTFUL HTTP请求类
*            一般用于模拟ajax
*/
class CPCHttpClient : public CPCNoCopyable
{
public:
    explicit CPCHttpClient()
    {
    }
    ~CPCHttpClient()
    {
        if (m_hSocketPtr)
            m_hSocketPtr->XClose();
    }

    /**
    *@brief     POST文件上传
    *@param     url             [in]    请求url
    *@param     filelist        [in]    要上传文件的文件路径列表，格式为 名称：文件路径
    *@param     nTimeOut        [in]    总超时 毫秒。如果nTimeoutMs < 0 代表永不超时(不建议)
    *@return    是否成功。 如果成功int为返回的httpcode,std::string为返回的body
    */
    CPCResult<std::pair<int,std::string> > UpLoadFile(const char* url, const std::vector<std::pair<std::string, std::string> >& filelist, int nTimeOut)
    {
        CPCHttpHeader header;
        header.SetKeepAlive(true);
        header.SetHeader("Accept", "text/html, application/xhtml+xml, */*");
        header.SetHeader("Accept-Language", "zh-CN");
        return UpLoadFile(url, filelist, nTimeOut, header);
    }

    /**
    *@brief     POST文件上传
    *@param     url             [in]    请求url
    *@param     filelist        [in]    要上传文件的文件路径列表，格式为 名称：文件路径
    *@param     nTimeOut        [in]    总超时 毫秒。如果nTimeoutMs < 0 代表永不超时(不建议)
    *@param     header          [in]    请求头
    *@return    是否成功。 如果成功int为返回的httpcode,std::string为返回的body
    */
    CPCResult<std::pair<int,std::string> > UpLoadFile(const char* url, const std::vector<std::pair<std::string, std::string> >& filelist, int nTimeOut, CPCHttpHeader& header)
    {
        CPCResult<std::pair<int,std::string> > result;
        if (url == NULL || url[0] == 0 )
        {
            return result.SetFail( "url == NULL || url[0] == 0 ");
        }

        //生成boundary包头
        std::string boundary = header.SetContentTypeBoundary();

        //生成boundary包体
        CPCFormDataBody body;
        body.Init(boundary);
        for (auto it = filelist.begin(); it != filelist.end(); ++it)
        {
            auto result2 = body.AppendFile((*it).first, (*it).second);
            if (!result2)
            {
                return result.SetFail("AppendFile fail:%s",result2.ErrDesc());
            }
        }

        auto result2 = HttpSendHeader(url, body.GetBodyContentLength(), nTimeOut, "POST", header);
		if (!result2)
        {
            return result.SetFail("HttpSendHeader fail:%s",result2.ErrDesc());
        }
        result2 = body.ScanBodyContent([this, &nTimeOut](const char* buf, size_t len)->CPCResult<>
        {
            return HttpSendContinue(buf, len, nTimeOut);
        });
        if (!result2)
        {
            return result.SetFail("ScanBodyContent fail:%s",result2.ErrDesc());
        }
        result2 = HttpRecv(nTimeOut);
        if (!result2)
        {
            return result.SetFail("HttpRecv fail:%s",result2.ErrDesc());
        }
        return result.SetSucc(std::make_pair( GetRetHead().GetRetCode(),GetRetBody()));
    }

    /**
    *@brief     GET支持断点续传的http/https下载文件。暂未支持增强校验ETag。不支持下载超过4GB的文件
    *@param     url             [in]    请求url
    *@param     filepath        [in]    要保存文件的文件路径
    *@param     nRetryCount     [in]    最大重试次数,如果连续失败大于这个限制，则会失败返回
    *@param     nRetryIntervalMs[in]    失败重试间隔 毫秒。失败重试间隔过短将会对服务器造成压力。
    *@param     callback        [in]    下载过程中的回调函数，如果下载成功，保证至少会回调一次
    *@param     nStartBytes     [in]    下载起始字节
    *@return    是否成功。 如果成功int为实际文件的字节数；std::string为服务器返回的文件名(如果有)
    */
    CPCResult<std::pair<int,std::string> > DownLoadFile(const char* url, const char* filepath, unsigned int nRetryCount = 3, unsigned int nRetryIntervalMs = 1000,
        std::function<CPCResult<>(unsigned int currbytes, unsigned int allbytes)> callback = nullptr,
        unsigned int nStartBytes = 0)
    {
        CPCHttpHeader header;
        header.SetUserAgent();
        header.SetKeepAlive(true);
        return this->DownLoadFile(url, header, filepath, nRetryCount, nRetryIntervalMs, callback, nStartBytes);
    }

    /**
    *@brief     GET支持断点续传的http/https下载文件。暂未支持增强校验ETag。不支持下载超过4GB的文件
    *@param     url             [in]    请求url
    *@param     header          [in]    请求头
    *@param     filepath        [in]    要保存文件的文件路径
    *@param     nRetryCount     [in]    最大重试次数,如果连续失败大于这个限制，则会失败返回
    *@param     nRetryIntervalMs[in]    失败重试间隔 毫秒。失败重试间隔过短将会对服务器造成压力。
    *@param     callback        [in]    下载过程中的回调函数，如果下载成功，保证至少会回调一次
    *@param     nStartBytes     [in]    下载起始字节
    *@return    是否成功。 如果成功IntResult()为实际文件的字节数；StrResult1()为服务器返回的文件名(如果有)
    */
    CPCResult<std::pair<int,std::string> >  DownLoadFile(const char* url, CPCHttpHeader& header, const char* filepath, unsigned int nRetryCount, unsigned int nRetryIntervalMs,
        std::function<CPCResult<>(unsigned int currbytes, unsigned int allbytes)> callback, unsigned int nStartBytes)
    {
        CPCResult<std::pair<int,std::string> > result;
        auto  result2 = CPCFileUtil::PCCheckAndCreateDir(filepath);
        if (!result2)
        {
            return result.SetFail("PCCheckAndCreateDir fail:%s",result2.ErrDesc());
        }
        FILE* fp = fopen(filepath, nStartBytes == 0 ? "wb" : "ab+");
        if (fp == NULL)
        {
            return result.SetSystemFail();
        }
        CPCAutoObj autoFile([&fp](){PCCloseFILE(fp); });

        CPCTimeValue  tvStart = CPCTimeValue::TickCount();
        unsigned int nRangeStart = nStartBytes;
        unsigned int nCurrentRetryCount = 0;
        while (1)
        {
            header.SetRangeBytes(nRangeStart, nRangeStart + PC_MAXHEAP);
            result2 = SendRequest(url, "", 600000, "GET", header);
            if (result2)
            {
                //成功，判断服务器是否支持
                CPCHttpHeader retheader = this->GetRetHead();
                unsigned int start, end, all; bool iscomplete;
                result2 = retheader.GetAcceptRangeBytesInfo(start, end, all, iscomplete);
                if (!result2)
                {
                    //解析出现异常
                    return result.SetFail("GetAcceptRangeBytesInfo fail:%s",result2.ErrDesc());
                }
                //判断接收的分块大小是不是和服务器回应的AcceptRange指定的分块数据大小相同，如果不相同则发生了丢失，需要重传
                std::string body = this->GetRetBody();
                if (retheader.GetRetCode() == 206 && body.length() != (end - start + 1))
                {
                    continue;
                }
                //如果服务器回应的start和客户端请求的start位置不同，需要移动文件指针
                if (start != nRangeStart)
                    fseek(fp, start, SEEK_SET);
                result2 = CPCFileUtil::PCWriteFile(fp, (const unsigned char*)body.data(), body.length());
                if (!result2)
                {
                    //写文件出现异常
                    return result.SetFail("PCWriteFile fail:%s",result2.ErrDesc());
                }
                //回调用户处理
                if (callback)
                {
                    if (retheader.GetRetCode() == 200 && all == 0 && iscomplete)
                        result2 = callback((unsigned int)body.length(), (unsigned int)body.length());
                    else
                        result2 = callback(end + 1, all);
                }
                if (!result2)
                {
                    //用户中止
                    return result.SetFail("callback fail:%s",result2.ErrDesc());
                }
                if (iscomplete)
                {
                    return result.SetSucc(std::make_pair(all,retheader.GetContentDisposition()));
                }
                nRangeStart = end + 1;
                nCurrentRetryCount = 0;
                continue;
            }
            nCurrentRetryCount++;
            if (nCurrentRetryCount > nRetryCount)
            {
                return result.SetFail("download fail and reached retry count limited(%u).", nRetryCount);
            }
            if (nRetryIntervalMs > 0)
            {
                PCSleep(nRetryIntervalMs);
            }
            continue;
        }
        return result;
    }

    /**
    *@brief     发送http/https请求并接收数据
    *@param     url         [in]    请求url
    *@param     body        [in]    请求数据，一般只有post有数据
    *@param     nTimeOut    [in]    超时毫秒。如果nTimeoutMs < 0 代表永不超时
    *@param     method      [in]    方法名，POST,GET,etc
    *@return    是否成功。 成功后用GetRetHead/GetRetBody函数取结果
    */
    CPCResult<> SendRequest(const char* url, const std::string& body, int nTimeOut, const char* method = "GET")
    {
        CPCHttpHeader header;
        //添加必要的默认http头
        if (method != NULL && strcmp(method, "POST") == 0)
            header.SetHeader("Content-Type", "application/x-www-form-urlencoded");
        header.SetUserAgent();
        header.SetKeepAlive(false);
        return this->SendRequest(url, body, nTimeOut, method, header);
    }

    /**
    *@brief     发送http/https请求并接收数据
    *@param     url         [in]    请求url
    *@param     body        [in]    请求数据，一般只有post有数据
    *@param     nTimeOut    [in]    超时毫秒。如果nTimeoutMs < 0 代表永不超时
    *@param     method      [in]    方法名，POST,GET,etc
    *@param     header      [in]    额外的http头
    *@return    是否成功。 成功后用GetRetHead/GetRetBody函数取结果
    */
    CPCResult<> SendRequest(const char* url, const std::string& body, int nTimeOut, const char* method, CPCHttpHeader& header)
    {
        auto result = HttpSendHeader(url, body.length(), nTimeOut, method, header);
        if (!result)
        {
            return result;
        }
        result = HttpSendContinue(body.data(), body.length(), nTimeOut);
        if (!result)
        {
            return result;
        }
        result = HttpRecv(nTimeOut);
        if (!result)
        {
            return result;
        }
        if (!header.IsKeepAlive())
        {
            m_hSocketPtr->XClose();
        }
        return result;
    }

    /**
    *@brief     发送http/https请求头，不发送body也不接收，但需要指明body长度，因为body在header中有Content-Length项
    *@param     url        [in]    请求url
    *@param     bodylen    [in]    body长度
    *@param     nTimeOut   [in]    超时毫秒。如果nTimeoutMs < 0 代表永不超时
    *@param     method     [in]    方法名，POST,GET,etc
    *@param     header     [in]    额外的http头
    *@return    是否成功。 成功后用GetRetHead/GetRetBody函数取结果
    */
    CPCResult<> HttpSendHeader(const char* url, size_t bodylen, int nTimeOut, const char* method, CPCHttpHeader &header)
    {
        CPCHttpUrl httpurl;
        auto result = httpurl.Parse(url);
        if (!result)
        {
            return result;
        }
        bool bssl = false;
        if (strcasecmp(httpurl.m_Schema.c_str(), "https") == 0)
        {
            bssl = true;
        }
        if (method == NULL)
        {
            return result.SetFail("method is NULL.");
        }

        CPCSockAddr addr;
        result = addr.GetHostByName(httpurl.m_Host.c_str(), httpurl.m_Port);
        if (!result)
        {
            return result;
        }

        //这次爬取到了另外的主机，关闭之前的连接并重新创建一个
        if (!m_hSocketPtr || !m_httpurl.SameAddr(httpurl) || m_hSocketPtr->Closed())
        {
            m_httpurl = httpurl;
            if (m_hSocketPtr)
                m_hSocketPtr->XClose();
            m_hSocketPtr = std::make_shared<CPCSocketHandle>(true, &CPCPoller::obj());
            auto result2 = m_hSocketPtr->XConnect(addr, nTimeOut, bssl);
            if (!result2)
            {
                return result.SetFail("XConnect fail:%s",result2.ErrDesc());
            }
        }

        //组织并发送头
        //header.SetRequestLine(method, url);    //这样在偶尔某些服务器上会导致url错误
        header.SetRequestLine(method, httpurl);
        header.SetHost(httpurl);
        header.SetContentLength(bodylen);
        std::string strheader = header.ToString(true, true);
        return HttpSendContinue(strheader.data(), strheader.length(), nTimeOut);
    }

    /**
    *@brief     可继续发送数据
    *@param     data        [in]    数据
    *@param     nTimeoutMs  [in]    超时时间毫秒，<0代表永不超时
    *@return    是否成功。
    */
    CPCResult<> HttpSendContinue(const char* data, size_t length, int nTimeoutMs)
    {
        CPCResult<> result;
        if (!m_hSocketPtr)
        {
            return result.SetFail("not inited.");
        }
        if (data == NULL || length == 0)
        {
            //空数据，不发
            return result;
        }
        return m_hSocketPtr->XSend(data, length, nTimeoutMs);
    }

    /**
    *@brief     接收数据
    *@param     nTimeOut[in]    超时毫秒。如果nTimeoutMs < 0 代表永不超时
    *@return    是否成功。
    */
    CPCResult<> HttpRecv(int nTimeOut)
    {
        CPCResult<> result;
        if (!m_hSocketPtr)
        {
            return result.SetFail("m_hSocketPtr not inited.");
        }
        m_UnPack.UnPackInit(false);
        while (1)
        {
            CPCBuffer package(PC_MAXSTACK);
            result = m_hSocketPtr->XRecv(nTimeOut, package);
            if (!result && m_UnPack.Empty())
            {
                m_hSocketPtr->XClose();
                return result;
            }
            if(!result)
            {
                //有数据，被远程关闭连接，可以当成功
                return result.SetSucc();
            }
            m_UnPack.UnPackUpdate(package.data(), package.length());
            if(!m_UnPack.m_LastResult)
            {
                //出错
                m_hSocketPtr->XClose();
                return result.SetFail("%s",m_UnPack.m_LastResult.ErrDesc());
            }
            else if (0 == m_UnPack.m_LastResult.Get())
            {
                //继续处理
                continue;
            }
            else 
            {
                //成功
                break;
            }
        }
        return result;
    }

    /////////////////////////////////////////////////////////////
    //在这里取结果。必须成功之后才能取结果，且只能取一次
    /////////////////////////////////////////////////////////////
    /**
    *@brief     取http响应body
    *@return    响应body内容
    */
    std::string GetRetBody()    { return std::move(m_UnPack.m_body); }
    /**
    *@brief     取http响应头
    *@return    http响应头
    */
    CPCHttpHeader GetRetHead()    { return std::move(m_UnPack.m_header); }
    /**
    *@brief     取http响应头，不能在GetRetHead之后调用
    *@return    http响应头
    */
    std::string GetRetHeadString(){ return m_UnPack.GetHeaderString(); }

private:
    CPCHttpUrl           m_httpurl;
    CPCRestHttpUnPack    m_UnPack;
    CPCSocketHandlePtr   m_hSocketPtr;
};

/**
*@brief      RESTFUL HTTP连接类
*            这是一个抽象类，用户要实现Process接口
*            【本对象只能被智能指针管理】
*/
class CPCHttpConnection : public CPCSocketHandle
{
public:
    explicit CPCHttpConnection(int nWaitMs = 30000)
        :CPCSocketHandle(false, &CPCPoller::obj())
        , m_nWaitMs(nWaitMs)
        , m_useDefaultHeadTemplate(true){}

    /**
    *@brief     用户实现，处理请求
    *@param     request   [in]    请求数据，注意request数据本身有可能是错误的，要先判断
    *                             先用request.m_LastResult判断是否是正确的包
    *@return    回应数据。回应空代表不回应，关闭连接
    */
    virtual std::string Process(const  CPCRestHttpUnPack& request) = 0;

    /**
    *@brief        线程池中处理http数据
    */
    void OnService() override
    {
        CPCRestHttpUnPack UnPack;
        UnPack.UnPackInit(true);
        while (1)
        {
            CPCBuffer package(PC_MAXSTACK);
            auto result = XRecv(m_nWaitMs, package);
            if (!result)
            {
                XClose();
                break;
            }

            UnPack.UnPackUpdate(package.data(), package.length());
            if(!UnPack.m_LastResult || UnPack.m_LastResult.Get() != 0)
            {
                //完成或出错，都交给用户处理
                std::string retbody = this->Process(UnPack);
                if (this->Closed())
                    break;
                if (m_useDefaultHeadTemplate)
                {
                    char szHead[PC_MAXURL] = { 0 };
                    snprintf(szHead, sizeof(szHead) - 1, "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: %d\r\n\r\n", (int)retbody.length());
                    XSend(szHead, strlen(szHead), m_nWaitMs);
                }
                if (!retbody.empty())
                {
                    XSend(retbody.data(), retbody.length(), m_nWaitMs);
                }
                break;
            }

            //继续接收
            continue;
        }
    }

protected:
    int  m_nWaitMs;
    bool m_useDefaultHeadTemplate;
};



#endif    //___PCLIB_S_TCP___